package org.example.orm;

import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author hsc
 * @date 2024-02-03
 * @Description 重新设计一个处理类：可以帮助SqlSession做很多其他的事情，比如反射分析类型等等问题
 */
public class Handler {


    /**
     * 设计一个用来解析sql的方法
     *
     * @param sql
     * @return SQLAndKey对象 ==》装载ArrayList<key>,原生带问号形式的String-sql
     */
    public SQLAndKey parseSQL(String sql) {
        //解析：insert into users values(#{uid},#{uname},#{upass})
        //存储解析出来的原始sql：insert into users values(?,?,?)
        StringBuilder newSql = new StringBuilder();
        //存储解析出来的 key : uid  uname upass
        List<String> keyList = new ArrayList<>();
        //解析sql
        while (true) {
            //先获取到#{ }的位置
            int left = sql.indexOf("#{");
            int right = sql.indexOf("}");
            //判断这两个符号是否为一对
            if (left != -1 && right != -1 & left < right) {
                //sql拼接 截取#{前面的部分，存入newSQL中
                newSql.append(sql.substring(0, left));
                //在newSQL中拼接一个问号?
                newSql.append("?");
                //将#{key}这个key截取出来，存入keyList集合中
                keyList.add(sql.substring(left + 2, right));
            } else {
                //有key的已经处理完了，拼接sql中最后的内容
                newSql.append(sql);
                //跳出循环
                break;
            }
            //完成截取的sql，接下来替换剩下的sql
            sql = sql.substring(right + 1);
        }
        //将上述的两个变量组合成一个对象，统一返回
        return new SQLAndKey(newSql, keyList);
    }

    /**
     * 处理参数值的分析问题    PreparedStatement去拼接
     * Object obj---->  基础类型(int-Integer float-Float String)   domain类型(User)  map类型
     * 参数?pstat,obj,keyList          返回值?void
     *
     * @param preparedStatement
     * @param object
     * @param keyList
     * @throws SQLException
     */
    public void handleParameter(PreparedStatement preparedStatement, Object object, List<String> keyList) throws SQLException {
        //1.获取obj对象对应的那个Class类型(传递过来的操作条件参数)
        Class clazz = object.getClass();
        //2.通常clazz应该是如下三种类型
        //  基础类型(一个变量 int/float/double/String)
        //  map类型(多个问号值，还不是domain中原来有的属性---map中的key与#{key}对应)
        //  domain类型(多个问号值，刚好都是domain中自带的属性---属性名字与#{key}对应)
        if (clazz == int.class || clazz == Integer.class) {
            preparedStatement.setInt(1, (Integer) object);
        } else if (clazz == float.class || clazz == Float.class) {
            preparedStatement.setFloat(1, (Float) object);
        } else if (clazz == double.class || clazz == Double.class) {
            preparedStatement.setDouble(1, (Double) object);
        } else if (clazz == String.class) {
            preparedStatement.setString(1, (String) object);
        } else {
            //剩下只有两种可能
            //map类型
            if (object instanceof Map) {
                this.setMap(preparedStatement, object, keyList);
            } else {
                //domain类型 对象类型
                this.setObject(preparedStatement, object, keyList);
            }
        }
    }

    /**
     * 负责处理map类型的参数 domain
     *
     * @param preparedStatement
     * @param object
     * @param keyList
     */
    private void setObject(PreparedStatement preparedStatement, Object object, List<String> keyList) throws SQLException {
        try {
            Class clazz = object.getClass();
            for (int i = 0; i < keyList.size(); i++) {
                //先找key
                String key = keyList.get(i);
                //通过key反射找寻obj对象中对应的那个属性        取值
                Field field = clazz.getDeclaredField(key);
                //正常：field-->属性名-->对应的get方法名-->Method-->invoke
                field.setAccessible(true);
                Object value = field.get(object);
                //pstat拼值
                preparedStatement.setObject(i + 1, value);
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

    /**
     * 负责处理domain类型的参数
     *
     * @param preparedStatement
     * @param object
     * @param keyList
     */
    private void setMap(PreparedStatement preparedStatement, Object object, List<String> keyList) throws SQLException {
        //1.先将obj对象还原回map
        Map map = (Map) object;
        //2.遍历keyList，通过key去map中找值，让pstat赋值
        for (int i = 0; i < keyList.size(); i++) {
            //获取key值
            String key = keyList.get(i);
            //获取map中的值
            Object value = map.get(key);
            preparedStatement.setObject(i + 1, value);
        }
    }

    /**
     * 处理返回值类型的分析问题
     * ResultSet rs，Class resultType(domain,map,int/float/double/String)
     *
     * @param resultSet
     * @param resultType
     * @param <T>
     * @return
     */
    public Object handleResult(ResultSet resultSet, Class resultType) throws SQLException {
        Object object = null;
        if (resultType == int.class || resultType == Integer.class) {
            object = resultSet.getInt(1);
        } else if (resultType == float.class || resultType == Float.class) {
            object = resultSet.getFloat(1);
        } else if (resultType == double.class || resultType == Double.class) {
            object = resultSet.getDouble(1);
        } else if (resultType == String.class) {
            object = resultSet.getString(1);
        } else {
            //map类型的数据
            if (resultType == Map.class) {
                object = this.getMap(resultSet);
            } else {
                //domain类型的数据
                object = this.getObject(resultSet,resultType);
            }
        }
        return object;
    }

    /**
     *
     *负责分析rs结果集的列名，列值，存入domain中
     * @param resultSet
     * @return
     */
    private Object getObject(ResultSet resultSet, Class resultType) {
        Object result=null;
        //获取实例化对象
        try {
            result=resultType.newInstance();
            //分析rs结果集的所有列名，列值
            ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
            //遍历这个resultSetMetaData，获取全部的列，列名
            //注意返回的结果集中，索引的 i的位置从1开始
            for (int i = 1; i <=resultSetMetaData.getColumnCount(); i++) {
                //每次循环获取一个列名字-----mapKey
                String columnName = resultSetMetaData.getColumnName(i);
                //通过列名字，反射找寻对应的属性名
                Field field = resultType.getDeclaredField(columnName);
                //正常情况：通过属性-->set方法-->invoke()
                //通过直接操作私有属性的方式
                field.setAccessible(true);
                //直接给属性赋值了
                Object columnValue = resultSet.getObject(columnName);
                //通过列名，去rs中获取值-----mapValue
                field.set(result,columnValue);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        //将数据封装为对象之后返回对象
        return result;
    }

    /**
     *
     *负责分析rs结果集的列名，列值，存入map中
     * @param resultSet
     * @return
     */
    private Object getMap(ResultSet resultSet) throws SQLException {
        //创建一个map用来存储最终的信息
        HashMap<String, Object> result = new HashMap<>();
        //分析rs结果集的所有列名，列值
        ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
        //遍历这个resultSetMetaData，获取全部的列，列名
        for (int i = 0; i < resultSetMetaData.getColumnCount(); i++) {
            //每次循环获取一个列名字-----mapKey
            String columnName = resultSetMetaData.getColumnName(i);
            //通过列名，去rs中获取值-----mapValue
            Object columnValue = resultSet.getObject(columnName);
            //存入map中
            result.put(columnName, columnValue);
        }
        return result;

    }
}
